﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace DarwinIsDead {
    /// <summary>
    /// Represents a characteristic in a Zombie.
    /// </summary>
    /// <remarks>
    /// A Trait essentially acts as a behavior component to the
    /// <see cref="Zombie"/>. Movement, attacks, drawing, AI, etc. are performed
    /// by the traits, while the Zombie simply acts as a container.
    /// 
    /// The only thing the Zombie does uniquely is take damage.  If a Trait
    /// handled this, the <see cref="ZombieFactoryRough"/> would evolve it out
    /// pretty quickly.
    /// 
    /// All other traits are derived from this class.
    /// </remarks>
    /// <seealso cref="Zombie"/>
    public abstract class Trait : IDisposable, ICloneable {
        private int level;

        /// <summary>
        /// Reference to owning zombie
        /// </summary>
        public Zombie Owner { get; set; }

        /// <summary>
        /// Reference to world
        /// </summary>
        protected World World { get; private set; }

        /// <summary>
        /// Whether or not this trait is enabled.  A disabled trait will not
        /// update its state or render itself to the screen.
        /// </summary>
        public bool Enabled { get; set; }

        /// <summary>
        /// Constructs a new <see cref="Zombie"/> Trait.
        /// </summary>
        /// <remarks>
        /// New <see cref="Trait"/>s are constructed at Level 0.  Derived
        /// Traits must adjust the initial level in their own constructors.
        /// 
        /// New Traits are enabled by default.
        /// </remarks>
        /// <param name="owner">Reference to owning Zombie</param>
        /// <param name="world">Reference to the World in which this' owning
        /// Zombie lives</param>
        public Trait(Zombie owner=null, World world=null) {
            Owner = owner;
            World = world;

            Enabled = true;
            level = 0;
        }

        /// <summary>
        /// Returns a copy of this <see cref="Trait"/>.
        /// </summary>
        /// <remarks>
        /// Every Trait must have a Clone method, for use in the
        /// <see cref="ZombieFactory"/>.  This allows the Trait to
        /// pass on its characteristics to new instantiations.
        /// </remarks>
        /// <returns></returns>
        public abstract Object Clone();

        /// <summary>
        /// Initializes this trait.
        /// </summary>
        /// <remarks>
        /// Some initialization tasks, such as grabbing references to other
        /// Traits, or even other Entities, can't be done until everything
        /// has been instantiated and their constructors called.  This is
        /// where the Initialize method comes in.  Perform any of these
        /// post-instantiation operations in this override.
        /// </remarks>
        public virtual void Initialize() {
        }

        /// <summary>
        /// Release any resources you don't want handled by the garbage
        /// collector
        /// </summary>
        /// <remarks>
        /// This is called by the owning <see cref="Zombie"/> when it's
        /// destroyed by the <see cref="ZombieFactoryRough"/>.  Anything that
        /// shouldn't be left over (e.g. Bullet entities) should be cleaned
        /// up in here.
        /// </remarks>
        public virtual void Dispose() {
        }

        /// <summary>
        /// Retrieve or set this <see cref="Trait"/>'s level of advancement.
        /// Successful traits will have higher levels.
        /// </summary>
        public int Level {
            get { return level; }
            set {
                level = value;
                AdjustLevel(level);
            }
        }

        /// <summary>
        /// Defines changes in Trait strength when the Trait's level changes
        /// </summary>
        /// <remarks>
        /// When the <see cref="ZombieFactoryRough"/> decides to strengthen or
        /// weaken this Trait, this method is called.  Any changes to the Trait
        /// and its members should be defined here.
        /// 
        /// Changing <see cref="Trait.Level"/> calls this method.
        /// </remarks>
        /// <param name="level">Current level</param>
        protected virtual void AdjustLevel(int level) { }

        /// <summary>
        /// Collision behavior.
        /// </summary>
        /// <remarks>
        /// called when a collision between this
        /// <see cref="Trait"/>'s owning <see cref="Zombie"/> and another
        /// Entity occurs.  Override this to specify behaviors based on
        /// collision events.
        /// 
        /// The owning Zombie will propogate collision events to each of its
        /// Traits.
        /// 
        /// When overriding, try to only change this Trait's state.  Avoid
        /// changing others' state if possible.
        /// </remarks>
        /// <param name="other">>Reference to the other Entity in the collision
        /// </param>
        /// <param name="overlap">Overlap between this Entity and other's hit
        /// boxes</param>
        public virtual void Collision(Entity other, Rectangle overlap) {
        }

        /// <summary>
        /// Logic update
        /// </summary>
        /// <remarks>
        /// Only <see cref="Trait"/>s with <c><see cref="Trait.Enabled"/> ==
        /// true</c> are updated.  To define this Trait's update behavior,
        /// override <see cref="InternalUpdate(GameTime)"/>.
        /// </remarks>
        /// <seealso cref="InternalUpdate(GameTime)"/>
        /// <seealso cref="Enabled"/>
        /// <param name="gameTime">Time elapsed</param>
        public void Update(GameTime gameTime) {
            if (Enabled) {
                InternalUpdate(gameTime);
            }
        }

        /// <summary>
        /// Render
        /// </summary>
        /// Only <see cref="Trait"/>s with <c><see cref="Trait.Enabled"/> ==
        /// true</c> are drawn.  To define this Trait's rendering behavior,
        /// override <see cref="InternalDraw(SpriteBatch)"/>.
        /// <seealso cref="InternalDraw(SpriteBatch)"/>
        /// <seealso cref="Enabled"/>
        /// <param name="spriteBatch">Sprite Batch to draw textures to</param>.
        /// <param name="color">Color of draw</param>
        public void Draw(SpriteBatch spriteBatch, Color color) {
            if (Enabled) {
                InternalDraw(spriteBatch, color);
            }
        }

        /// <summary>
        /// Internal logic update
        /// </summary>
        /// <remarks>
        /// Override this method to define this <see cref="Trait"/>'s logic
        /// update behavior.
        /// </remarks>
        /// <param name="gameTime"></param>
        protected virtual void InternalUpdate(GameTime gameTime) { }

        /// <summary>
        /// Internal render
        /// </summary>
        /// <remarks>
        /// Override this method to define this <see cref="Trait"/>'s rendering
        /// behavior.
        /// </remarks>
        /// <param name="spriteBatch"></param>
        protected virtual void InternalDraw(SpriteBatch spriteBatch, Color color) { }
    }
}
